package cn.itcast.hotel;

import cn.itcast.hotel.pojo.HotelDoc;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public class EsSearchDemo {




    @Test //聚合
    void aggregations() throws IOException {
    //按照品牌进行分组，打印每个品牌的名称 并统计每个品牌的文档数量
        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        //2.组装请求
        SearchSourceBuilder source = request.source();
        source.size(0);
        source.aggregation(
                AggregationBuilders
                .terms("brand_agg")
                .field("brand")
                .size(20)
        );
        // 3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析响应
        Aggregations aggregations = response.getAggregations();
        Terms brandTerms= aggregations.get("brand_agg");
        List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            String brandName = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            System.out.println("品牌:"+brandName+" 数量:"+docCount);
        }

    }

    @Test //高亮
    void testHighlight() throws IOException {
        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();
        source.query(QueryBuilders.matchQuery("all", "如家"));
        //2.设置高亮
        source.highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
        // 3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析响应
        handleResponse2(response);

    }

    @Test //分页排序
    void testPageAndSort() throws IOException {
        // 页码，每页大小
        int page = 1, size = 8;

        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();
        source.query(QueryBuilders.matchAllQuery());
        source.sort("price", SortOrder.ASC);
        source.from((page - 1) * size).size(8);
        // 2.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 3.解析响应
        handleResponse(response);

    }

    @Test//bool查询
    public void testBool() throws IOException {
        //创建请求对象
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder source = request.source();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        boolQuery.must(QueryBuilders.matchQuery("name", "上海外滩"));
        boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));
        boolQuery.mustNot(QueryBuilders.termQuery("price","249"));
        source.query(boolQuery);
        //
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //
        handleResponse(response);
    }
    @Test//range查询（根据数值范围查询）
    public void testRange() throws IOException {
        //创建请求对象
        SearchRequest request = new SearchRequest("hotel");

        SearchSourceBuilder sourceBuilder = request.source();
        sourceBuilder.query(QueryBuilders.rangeQuery("price").gte(100).lte(300));
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //最后调用解析响应的方法
        handleResponse(response);
    }

    @Test//term查询 （根据词条精确查询）
    public void testTerm() throws IOException {
        //创建请求对象
        SearchRequest request = new SearchRequest("hotel");

        SearchSourceBuilder sourceBuilder = request.source();
        sourceBuilder.query(QueryBuilders.termQuery("city","北京"));
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //最后调用解析响应的方法
        handleResponse(response);
    }

    @Test//match查询 （全文检索查询）
    public void testMatch() throws IOException {
        //创建请求对象
        SearchRequest request = new SearchRequest("hotel");
        //准备DSL
        SearchSourceBuilder source = request.source();
        source.query(QueryBuilders.matchQuery("name", "如家"));
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //最后调用解析响应的方法
        handleResponse(response);
    }

    @Test//match_all查询 查询所有
    public void testMatchAll() throws IOException {
        //创建请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        //发送请求
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //最后调用解析响应的方法
        handleResponse(response);
    }

    //解析响应的方法
    private void handleResponse(SearchResponse response) {
        //获取第一个hits
        SearchHits hits = response.getHits();
        //获取总条数并打印
        System.out.println("共搜索到" + hits.getTotalHits().value + "条数据");
        //遍历数组获得文档列表
        for (SearchHit hit : hits) {
            //获取文档source
            String source = hit.getSourceAsString();
            //反序列化
            HotelDoc hotelDoc = JSON.parseObject(source, HotelDoc.class);
            System.out.println(hotelDoc);
        }
    }

    //解析高亮响应方法
    private void handleResponse2(SearchResponse response) {
        // 4.解析响应
        SearchHits hits = response.getHits();
        // 4.1.获取总条数
        long total = hits.getTotalHits().value;
        System.out.println("共搜索到" + total + "条数据");
        // 4.3.遍历
        for (SearchHit hit : hits) {
            // 获取文档source
            String json = hit.getSourceAsString();
            // 反序列化
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
            // 获取高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                // 根据字段名获取高亮结果
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null) {
                    // 获取高亮值
                    String name = highlightField.getFragments()[0].string();
                    // 覆盖非高亮结果
                    hotelDoc.setName(name);
                }
            }
            System.out.println("hotelDoc = " + hotelDoc);
        }
    }

    //首先创建连接对象
    RestHighLevelClient client;

    @BeforeEach
    public void init() {
        // 1. 准备好Es的客户端对象
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.200.130:9200")
        ));
    }

    @AfterEach
    public void close() {
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
